import info.gridworld.grid.*;
import java.util.ArrayList;
import java.util.*;

public class UnBoundedGrid2<E> extends AbstractGrid<E> {

    // the array storing the grid elements
    private Object[][] occArray;
    private int size;

    // Constructer
    public UnBoundedGrid2() {
        size = 16;
        occArray = new Object[size][size];
    }

    // get method for class
    public int getNumRows() {
        return -1;
    }

    public int getNumCols() {
        return -1;
    }

    // Judge the location is vaild
    public boolean isValid(Location loc) {
        int r = loc.getRow();
        int c = loc.getCol();

        // UnBounded
        if ((r >= 0) && (c >= 0)) {
            return true;
        }
        return false;
    }

    // return the location of that the number of grid occupant
    // return_type: ArrayList<Location>
    public ArrayList<Location> getOccupiedLocations() {
        ArrayList<Location> locs = new ArrayList<Location>();

        // traverse the locations of grid
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                Location loc = new Location(i, j);
                if (get(loc) != null) {
                    locs.add(loc);
                }
            }
        }
        return locs;
    }

    //
    public E get(Location loc) {
        if (!isValid(loc)) {
            throw new IllegalArgumentException("Error! Location: " + loc + " is InValid.");
        }
        if (loc.getRow() >= size || loc.getCol() >= size) {
            return null;
        }

        int r = loc.getRow();
        int c = loc.getCol();
        if (occArray[r][c] != null) {
            return (E) occArray[r][c];
        }
        return null;

    }

    public E put(Location loc, E obj) {
        if (loc == null) {
            throw new IllegalArgumentException("Error! Location: " + loc + " is InValid.");
        }
        if (obj == null) {
            throw new IllegalArgumentException("Error! obj == null");
        }

        /*
         * the grid need to resize if the row or col of loc is greather than size.
         */
        if (loc.getRow() >= size || loc.getCol() >= size) {
            resize(loc);
        }

        E old = get(loc);
        // put the new one, return old
        occArray[loc.getRow()][loc.getCol()] = obj;
        return old;
    }

    public E remove(Location loc) {
        if (!isValid(loc)) {
            throw new IllegalArgumentException("Error! Location: " + loc + " is InValid.");
        }
        if (loc.getRow() >= size || loc.getCol() >= size) {
            return null;
        }

        E old = get(loc);
        occArray[loc.getRow()][loc.getCol()] = null;
        return old;
    }

    private void resize(Location loc) {
        int newSize = size;
        while (loc.getRow() >= newSize || loc.getCol() >= newSize) {
            newSize *= 2;
        }

        // create a bigger occArray to store
        Object[][] newArray = new Object[newSize][newSize];

        // traverse old occArray
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                newArray[i][j] = occArray[i][j];
            }
        }

        // reset member var
        occArray = newArray;
        size = newSize;
    }
}